package org.nicocube.airain.domain.server.criteria;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.db4o.query.Candidate;
import com.db4o.query.Evaluation;
import com.db4o.query.Query;

/**
 * Build a complex query constraint for finding all the object that has the value of a column between a certain range
 * 
 * @author nicolas
 * @version 0.1
 * 
 * @param <T>
 */
public class BetweenCriteria<T extends Comparable<T>> extends AbstractCriteria {

	private static final Logger log = LoggerFactory.getLogger(BetweenCriteria.class);
	
	private T up;
	private T down;
	private Class<T> clazz = null;

	/**
	 * Build the Criteria. This constructor is a shortcut and more efficient implementation for primitive type value.
	 * 
	 * @param column the column to set the Constraint on
	 * @param down the inclusive lower boundary
	 * @param up the inclusive upper boundary
	 */	
	public BetweenCriteria(String column, T down, T up) {
		super(column);
		this.up = up;
		this.down = down;
	}
	/**
	 * Build the Criteria for {@link Comparable} Object of type Class&lt;T&gt;.
	 * 
	 * @param column the column to set the Constraint on
	 * @param clazz 
	 * @param down
	 * @param up
	 */	
	public BetweenCriteria(Class<T> clazz, String column, T down, T up) {
		this(column, down, up);
		this.clazz = clazz;
	}

	/**
	 * The upper boundary
	 * @return
	 */
	public T getUp() {
		return up;
	}

	/**
	 * The lower boundary
	 * @return
	 */
	public T getDown() {
		return down;
	}

	@Override
	public void apply(Query q) {
		if (log.isDebugEnabled()) log.debug("clazz:"+clazz);
		if (clazz == null) {
			q.descend(getColumn()).constrain(getDown()).greater().equal()
					.and(
							q.descend(getColumn()).constrain(getUp()).smaller()
									.equal());
		} else {
			if (log.isDebugEnabled()) log.debug("eval ici");
			q.descend(getColumn()).constrain(new Evaluation() {
				private static final long serialVersionUID = 1L;
				@Override
				public void evaluate(Candidate candidate) {
					T c = clazz.cast(candidate.getObject()) ;
					candidate.include(c.compareTo(down) >= 0 && c.compareTo(up) <= 0);
				}
			});
		}
	}
}
